package com.gitee.feizns.quickstart.log;

import com.gitee.feizns.quickstart.spring.expression.SpEl;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 操作日志拦截器
 * @author feizns
 * @since 2024/2/25
 */
@Aspect
public class LogAroundAspect {

    /**
     * 监听器
     */
    private final List<LogAroundAspectListener> listeners;

    /**
     * 日志用户编号供应商
     */
    private final LogUserIdSupplier logUserIdSupplier;

    /**
     * 监听器列表
     */
    public LogAroundAspect(LogUserIdSupplier logUserIdSupplier, List<LogAroundAspectListener> listeners) {
        this.logUserIdSupplier = logUserIdSupplier;
        this.listeners = listeners;
    }

    /**
     * 拦截标记web操作注解的函数
     * @param joinPoint {@link ProceedingJoinPoint}
     * @return {@link Object}
     */
    @Around("@annotation(com.gitee.feizns.quickstart.log.Log)")
    public Object aroundLogAnnotationMethod(ProceedingJoinPoint joinPoint) {
        //封装操作日志
        MethodLog log = new MethodLog();
        log.setArgs(joinPoint.getArgs());
        log.setTime(LocalDateTime.now());
        //获取操作日志注解
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Log annotation = method.getAnnotation(Log.class);
        log.setType(annotation.type());
        //执行异常
        Object result = null;
        //执行函数
        try {
            result = joinPoint.proceed();
            log.setResult(result);
        } catch (Throwable e) {
            log.setException(e);
        }
        //表达式上下文
        Map<String, Object> context = log.context();
        //将方法请求参数根据其名称也放置到解析上下文中
        for (int i = 0; i < signature.getParameterNames().length; i++)
            context.put(signature.getParameterNames()[i], log.getArgs()[i]);
        //解析操作描述表达式
        String description = Objects.toString(SpEl.execute(annotation.value(), context));
        log.setDescription(description);
        //解析用户编号
        Object userid = null;
        if ( logUserIdSupplier != null )
            userid = logUserIdSupplier.get();
        if ( userid == null && StringUtils.hasText(annotation.userid()) )
            userid = SpEl.execute(annotation.userid(), context);
        log.setUserid(userid);
        //调用监听函数
        listeners.stream()
                .peek(listener -> listener.listener(log))
                .forEach(listener -> listener.around(joinPoint));
        //将原异常抛出
        if ( log.hasException() )
            throw new RuntimeException(log.exceptionMessage());
        //返回函数执行结果
        return result;
    }

}
